package com.triones.yjj.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.*;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.ruoyi.common.config.YjjExternalAuthConfig;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtil;
import com.ruoyi.common.utils.HttpUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.triones.yjj.entity.*;
import com.triones.yjj.entity.bo.WeChatMiniBindPhoneBO;
import com.triones.yjj.entity.bo.WeChatMiniLoginBO;
import com.triones.yjj.entity.bo.WeChatMiniUserInfoBO;
import com.triones.yjj.entity.vo.*;
import com.triones.yjj.mapper.*;
import com.triones.yjj.service.WeChatManager;
import lombok.RequiredArgsConstructor;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.time.Duration;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author cww
 * @version 1.0
 * 2024/7/11 14:34
 */
@Service
@RequiredArgsConstructor
public class WeChatManagerImpl implements WeChatManager {

    private static transient Logger logger = LoggerFactory.getLogger(WeChatManagerImpl.class);

    private final YjjExternalAuthConfig yjjExternalAuthConfig;

    private final WxUserMapper wxUserMapper;

    private static final String WX_MINI_SESSIONKEY="{WX}{MINI}{SESSION_KEY}_";

    private final MemberMapper memberMapper;

    private static final String WX_MINI_SESSION_MEMBER="{WX}{MINI}{SESSION_MEMBER}_";

    private static final String WX_MINI_SESSION_STAFF="{WX}{MINI}{SESSION_STAFF}_";

    private static final Integer WX_TOKEN_VAILD_TIME_MINI = 60*60*24*2;

    private final StaffMapper staffMapper;

    private final ShareRecordMapper shareRecordMapper;

    private static final String WX_MINI_ACCESS_TOKEN="{WX}{MINI}{ACCESS_TOKEN}_";

    private final ActivityMapper activityMapper;

    public Map miniLogin(WeChatMiniLoginBO weChatMiniLoginBO) {

        Map res = new HashMap(16);

        String content = wxMiniAutoCode(weChatMiniLoginBO.getCode());
        if (StringUtils.isEmpty(content)){
            res.put("code", "fail");
            res.put("reason", "auth_code_fail");
            return res;
        }
        //校验是否绑定活动
        JSONObject json = JSONUtil.parseObj(content);;
        logger.info("miniLogin==json==="+json.toString());
        String openid = json.getStr("openid");
        // 获取会话密钥（session_key）
        String sessionKey = json.get("session_key").toString();
        RedisUtils.setCacheObject(WX_MINI_SESSIONKEY+openid,sessionKey, Duration.ofDays(2));
        //校验用户是否存在
        WxUserVO wxUserVO = getWxUserByOpenId(openid);
        if(ObjectUtil.isEmpty(wxUserVO)){
            //获取不到unionid
            JSONObject userInfoJson = getUserInfo(weChatMiniLoginBO.getEdata(), sessionKey, weChatMiniLoginBO.getIv());
            logger.info("miniLogin==userInfoJson==="+userInfoJson.toString());
            if (userInfoJson.get("unionId")==null){
                res.put("code", "fail");
                res.put("reason", "fail_to_get_unionid");
                return res;
            }
            WxUserDO wxUserDO = new WxUserDO();
            wxUserDO.setAvatarUrl(userInfoJson.getStr("avatarUrl")==null?null:userInfoJson.getStr("avatarUrl"));
            //wxUserDO.setNickname(userInfoJson.getStr("nickName")==null?null:userInfoJson.getStr("nickName"));
            String nickname = userInfoJson.getStr("nickName");
          /*  String tmpStr = "";
            if(ObjectUtil.isNotEmpty(nickname)){
                 tmpStr = nickname.replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", "*");
            }
            wxUserDO.setNickname(tmpStr);*/
            wxUserDO.setNickname(nickname);

            wxUserDO.setOpenId(openid);
            wxUserDO.setUnionId(userInfoJson.getStr("unionId"));
            wxUserDO.setCreateTime(DateUtil.getDateTimeString());
            wxUserMapper.insert(wxUserDO);
        }
        res.put("code","success");
        return res;
    }
    public R<Map> getOpenid(String code) {
        Map res = new HashMap();
        String content = wxMiniAutoCode(code);
        if (StringUtils.isEmpty(content)){
            return R.fail("auth_code_fail");
        }
        JSONObject json = JSONUtil.parseObj(content);
        logger.info("getMiniOpenid==json==="+json.toString());
        String errcode = json.getStr("errcode");
        if (ObjectUtil.isEmpty(errcode)){
            String openid = json.getStr("openid");
            String unionid = json.getStr("unionid");
            String sessionKey = json.getStr("session_key");//会话秘钥
            res.put("code", "success");
            res.put("openid", openid);
            res.put("session_key", sessionKey);
            RedisUtils.setCacheObject(WX_MINI_SESSIONKEY+openid,sessionKey, Duration.ofDays(2));
            WxUserVO wxUserVO = getWxUserByOpenId(openid);
            boolean isStaff = false;
            if(ObjectUtil.isEmpty(wxUserVO)){
                wxUserVO = new WxUserVO();
                WxUserDO wxUserDO = new WxUserDO();
                wxUserDO.setOpenId(openid);
                wxUserDO.setUnionId(unionid);
                wxUserDO.setCreateTime(DateUtil.getDateTimeString());
                wxUserMapper.insert(wxUserDO);
                BeanUtil.copyProperties(wxUserDO,wxUserVO);
            }

            res.put("wxUser", wxUserVO);

            MemberVO memberVO =  getMemberInfoByWxUserId(wxUserVO.getId());
            Long memberId = 0l;
            Long latelyActivityId = 0l;
            if(ObjectUtil.isNotEmpty(memberVO)){
                memberId = memberVO.getId();
                latelyActivityId = memberVO.getLatelyActivityId();
            }
            res.put("memberId", memberId);

            ActivityDO activityDO = activityMapper.selectOne(new LambdaQueryWrapper<ActivityDO>()
                .eq(ActivityDO::getId,latelyActivityId)
                .eq(ActivityDO::getDeleted,0)
                .eq(ActivityDO::getIsDraft,0));
            res.put("latelyActivityId", "");
            if (activityDO != null) {
                res.put("latelyActivityId", latelyActivityId);
            }
            //分享id
            ShareRecordVO shareRecordVO = getLastShareRecord(memberId);
            Long shareId = 0l;
            Long shareRecordId = 0l;
            Long staffId = 0l;
            if(ObjectUtil.isNotEmpty(shareRecordVO)){
                shareId = shareRecordVO.getShareId();
                shareRecordId = shareRecordVO.getId();
                staffId = shareRecordVO.getShareStaffId();
                //是否是商家的员工
                StaffVO staffVO = getDetailByWxId(wxUserVO.getId(),shareRecordVO.getUserId());
                if(ObjectUtil.isNotEmpty(staffVO)){
                    isStaff = true;
                }
            }
            res.put("shareId", shareId);
            res.put("shareRecordId", shareRecordId);
            res.put("staffId", staffId);

            res.put("isStaff", isStaff);
            //登录
            return  R.ok("成功",res);
        }else {
            return R.fail(json.getStr("errmsg"));
        }

    }
    public R<Map> getMiniOpenid(String code,Long userId) {
        Map res = new HashMap();
        String content = wxMiniAutoCode(code);
        if (StringUtils.isEmpty(content)){
            return R.fail("auth_code_fail");
        }
        JSONObject json = JSONUtil.parseObj(content);
        logger.info("getMiniOpenid==json==="+json.toString());
        String errcode = json.getStr("errcode");
        if (ObjectUtil.isEmpty(errcode)){
            String openid = json.getStr("openid");
            String unionid = json.getStr("unionid");
            String sessionKey = json.getStr("session_key");//会话秘钥
            res.put("code", "success");
            res.put("openid", openid);
            res.put("session_key", sessionKey);
            RedisUtils.setCacheObject(WX_MINI_SESSIONKEY+openid,sessionKey, Duration.ofDays(2));
            WxUserVO wxUserVO = getWxUserByOpenId(openid);
            boolean isStaff = false;
            if(ObjectUtil.isEmpty(wxUserVO)){
                wxUserVO = new WxUserVO();
                WxUserDO wxUserDO = new WxUserDO();
                wxUserDO.setOpenId(openid);
                wxUserDO.setUnionId(unionid);
                wxUserDO.setCreateTime(DateUtil.getDateTimeString());
                wxUserMapper.insert(wxUserDO);
                BeanUtil.copyProperties(wxUserDO,wxUserVO);
            }else{
                //是否是商家的员工
                StaffVO staffVO = getDetailByWxId(wxUserVO.getId(),userId);
                if(ObjectUtil.isNotEmpty(staffVO)){
                    isStaff = true;
                }
            }
            res.put("isStaff", isStaff);
            res.put("wxUser", wxUserVO);

            MemberVO memberVO =  getMemberInfoByWx(userId,wxUserVO.getId());
            Long memberId = 0l;
            Long latelyActivityId = 0l;
            if(ObjectUtil.isEmpty(memberVO)){
                //添加member
                MemberDO memberDO = new MemberDO();
                memberDO.setWxUserId(wxUserVO.getId());
                memberDO.setUserId(userId);
                memberDO.setCreateTime(DateUtil.getDateTime());
                memberDO.setName(wxUserVO.getNickname());
                memberDO.setMobile(wxUserVO.getMobile());
                memberMapper.insert(memberDO);
                memberId = memberDO.getId();
            }else{
                memberId = memberVO.getId();
                latelyActivityId = memberVO.getLatelyActivityId();
            }
            res.put("memberId", memberId);
            res.put("latelyActivityId", latelyActivityId);
            //登录
            return  R.ok("成功",res);
        }else {
            return R.fail(json.getStr("errmsg"));
        }

    }
    public StaffVO getDetailByWxId(Long wxUserId,Long userId) {
        QueryWrapper<StaffDO> wrapper = Wrappers.query();
        wrapper.eq("user_id",userId);
        wrapper.eq("wx_user_id",wxUserId);
        wrapper.eq("deleted",0);
        return staffMapper.selectVoOne(wrapper);
    }
    public R<WxUserVO> userInfo(WeChatMiniUserInfoBO weChatMiniUserInfoBO) {
        //校验用户是否存在
        WxUserVO wxUserVO = getWxUserByOpenId(weChatMiniUserInfoBO.getOpenId());
        if(ObjectUtil.isNotEmpty(wxUserVO)){
            WxUserDO wxUserDO = new WxUserDO();
            BeanUtil.copyProperties(wxUserVO,wxUserDO);
            wxUserDO.setAvatarUrl(weChatMiniUserInfoBO.getAvatarUrl());
            String nickname = weChatMiniUserInfoBO.getNickName();
//            String tmpStr = nickname.replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", "*"); //将emoji的unicode置为空，其他不动
//            wxUserDO.setNickname(tmpStr);
            wxUserDO.setNickname(weChatMiniUserInfoBO.getNickName());
            wxUserMapper.updateById(wxUserDO);
            wxUserVO.setAvatarUrl(weChatMiniUserInfoBO.getAvatarUrl());
            wxUserVO.setNickname(weChatMiniUserInfoBO.getNickName());
            return R.ok("用户信息设置成功",wxUserVO);
        }else{
            return R.fail("用户不存在");
        }
    }

    public Map miniBindPhone(WeChatMiniBindPhoneBO weChatMiniBindPhoneBO,MemberDetailVO memberDetailVO) {
        Map res = new HashMap(16);
        WxUserVO wxUserVO = getWxUserByOpenId(weChatMiniBindPhoneBO.getOpenId());
        if(ObjectUtil.isEmpty(wxUserVO)){
            res.put("bindPhone","fail");
            res.put("reason","未找到用户信息");
            return res;
        }
        String sessionKey = RedisUtils.getCacheObject(WX_MINI_SESSIONKEY + weChatMiniBindPhoneBO.getOpenId());
        if (StringUtils.isEmpty(sessionKey)){
            res.put("bindPhone","fail");
            res.put("reason","fail_to_get_sessionKey");
            return res;
        }
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] decodedSessionKey = decoder.decode(sessionKey);
            byte[] decodedIv =decoder.decode(weChatMiniBindPhoneBO.getIv());
            byte[] decodedEncryptedData=decoder.decode(weChatMiniBindPhoneBO.getEncryptedData());
            String decryptPhoneStr = decrypt(decodedSessionKey,decodedIv,decodedEncryptedData);
            JSONObject jsonObject = JSONUtil.parseObj(decryptPhoneStr);
            if (jsonObject.get("phoneNumber")!=null){
                String mobile = jsonObject.getStr("phoneNumber");
                //绑定手机号
                WxUserDO wxUserDO = new WxUserDO();
                BeanUtil.copyProperties(wxUserVO,wxUserDO);
                wxUserDO.setMobile(mobile);
                wxUserMapper.updateById(wxUserDO);

                //修改member
                QueryWrapper<MemberDO> wrapper = Wrappers.query();
                MemberDO memberDO = new MemberDO();
                wrapper.eq("wx_user_id",wxUserVO.getId());
                wrapper.eq("user_id",memberDetailVO.getUserId());
                wrapper.eq("deleted",0);
                memberDO.setMobile(mobile);
                memberMapper.update(memberDO,wrapper);
                //重新缓存member
                MemberDetailVO memberDetail =  memberMapper.selectDetailById(memberDetailVO.getId());
                RedisUtils.setCacheObject(WX_MINI_SESSION_MEMBER+weChatMiniBindPhoneBO.getToken(),memberDetail, Duration.ofDays(2));
                res.put("bindPhone","success");
                res.put("phone",mobile);
                wxUserVO.setMobile(mobile);
                res.put("wxUser",wxUserVO);
                return res;
            }else{
                res.put("bindPhone","fail");
                res.put("reason","fail_to_find_mobile_from_decrypt_data");
                return res;
            }
        } catch (Exception e) {
            e.printStackTrace();
            res.put("bindPhone","fail");
            res.put("reason",e.getMessage());
            return res;
        }
    }

    public static String decrypt(byte[] key, byte[] iv, byte[] encData) throws Exception {
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        //解析解密后的字符串
        return new String(cipher.doFinal(encData),"UTF-8");
    }

    /**
     * 登录凭证校验
     * @param code
     * @return
     */
    private String wxMiniAutoCode(String code) {
        String appId = yjjExternalAuthConfig.getAppid();
        String appKey = yjjExternalAuthConfig.getSecretKey();
        String url = "https://api.weixin.qq.com/sns/jscode2session?" +
            "appid=" + appId + "&" +
            "secret=" + appKey + "&" +
            "js_code=" + code + "&" +
            "grant_type=authorization_code";
        return HttpUtils.doGet(url, "UTF-8", 100, 1000);
    }

    /**
     * 解密，获取信息
     * @param encryptedData
     * @param sessionKey
     * @param iv
     * @return
     */
    public JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.getDecoder().decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.getDecoder().decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.getDecoder().decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            // 初始化
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSONUtil.parseObj(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public WxUserVO getWxUserByOpenId(String openId) {
        QueryWrapper<WxUserDO> wrapper = Wrappers.query();
        wrapper.eq("open_id",openId);
        return wxUserMapper.selectVoOne(wrapper);
    }
    public MemberVO getMemberInfoByWx(Long userId,Long wxUserId) {
        QueryWrapper<MemberDO> wrapper = Wrappers.query();
        wrapper.eq("wx_user_id",wxUserId);
        wrapper.eq("user_id",userId);
        wrapper.eq("deleted",0);
        wrapper.orderByDesc("update_time ");
        wrapper.last(" limit 1");
        return memberMapper.selectVoOne(wrapper);
    }
    public MemberVO getMemberInfoByWxUserId(Long wxUserId) {
        QueryWrapper<MemberDO> wrapper = Wrappers.query();
        wrapper.eq("wx_user_id",wxUserId);
        wrapper.eq("deleted",0);
        wrapper.orderByDesc("update_time ");
        wrapper.last(" limit 1");
        return memberMapper.selectVoOne(wrapper);
    }
    public WxMemberVO memberWxLogin(ActivityWxInfoVO activityWxInfoVO, String openId){
        WxUserVO wxUserVO = getWxUserByOpenId(openId);
        if(ObjectUtil.isNotEmpty(wxUserVO)){
            MemberVO memberVO =  getMemberInfoByWx(activityWxInfoVO.getUserId(),wxUserVO.getId());
         /*   if(ObjectUtil.isEmpty(memberVO)){
                throw new ServiceException("客户不存在");
            }*/
            Long memberId = 0l;
            if(ObjectUtil.isEmpty(memberVO)){
                //添加member
                MemberDO memberDO = new MemberDO();
                memberDO.setWxUserId(wxUserVO.getId());
                memberDO.setUserId(activityWxInfoVO.getUserId());
                memberDO.setCreateTime(DateUtil.getDateTime());
                memberDO.setName(wxUserVO.getNickname());
                memberDO.setMobile(wxUserVO.getMobile());
                memberDO.setUpdateTime(DateUtil.getDateTime());
                memberDO.setLatelyActivityId(activityWxInfoVO.getId());
                memberMapper.insert(memberDO);
                memberId = memberDO.getId();
            }else{
                //更新最近活动ID
                new LambdaUpdateChainWrapper<>(this.memberMapper)
                    .set(MemberDO::getLatelyActivityId, activityWxInfoVO.getId())
                    .set(MemberDO::getUpdateTime, DateUtil.getDateTime())
                    .eq(MemberDO::getId, memberVO.getId())
                    .update();
                memberId = memberVO.getId();
            }
            //查询member
            MemberDetailVO memberDetailVO =  memberMapper.selectDetailById(memberId);
            if(ObjectUtil.isNotEmpty(memberDetailVO)){
                WxMemberVO wxMemberVO = new WxMemberVO();
                wxMemberVO.setMemberDetailVO(memberDetailVO);
                String token = generateToken();
                RedisUtils.setCacheObject(WX_MINI_SESSION_MEMBER+token,memberDetailVO, Duration.ofDays(2));
                wxMemberVO.setToken(token);
                return wxMemberVO;
            }

        }
        return null;
    }

    private static String generateToken() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    public MemberDetailVO getCacheMember(String token){
        MemberDetailVO memberDetailVO = RedisUtils.getCacheObject(WX_MINI_SESSION_MEMBER + token);
        return memberDetailVO;
    }


    public WxStaffVO staffWxLogin(Long userId, String openId){
        WxUserVO wxUserVO = getWxUserByOpenId(openId);
        if(ObjectUtil.isNotEmpty(wxUserVO)){
            WxStaffDetailVO wxStaffDetailVO = staffMapper.selectWxStaffDetailById(wxUserVO.getId(),userId);
            if(ObjectUtil.isNotEmpty(wxStaffDetailVO)){
                WxStaffVO wxStaffVO = new WxStaffVO();
                wxStaffVO.setWxStaffDetailVO(wxStaffDetailVO);
                String token = generateToken();
                RedisUtils.setCacheObject(WX_MINI_SESSION_STAFF+token,wxStaffDetailVO, Duration.ofDays(2));
                wxStaffVO.setToken(token);
                return wxStaffVO;
            }
        }
        return null;
    }
    public WxStaffDetailVO getCacheStaff(String token){
        WxStaffDetailVO wxStaffVO = RedisUtils.getCacheObject(WX_MINI_SESSION_STAFF + token);
        return wxStaffVO;
    }

    /**
     * 更新员工信息后，更新缓存
     * @param token
     * @param wxStaffDetailVO
     */
    public void reCacheStaff(String token,WxStaffDetailVO wxStaffDetailVO){
        RedisUtils.setCacheObject(WX_MINI_SESSION_STAFF+token,wxStaffDetailVO, Duration.ofDays(2));
    }
    public ShareRecordVO getLastShareRecord(Long memberId){
        QueryWrapper<ShareRecordDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("passive_member_id",memberId);
        queryWrapper.last(" limit 1");
        queryWrapper.orderByDesc("create_time");
        return shareRecordMapper.selectVoOne(queryWrapper);
    }

    /**
     * 获取接口调用凭据
     * @return
     */
    private String getAccessToken() {
        String appId = yjjExternalAuthConfig.getAppid();
        String appKey = yjjExternalAuthConfig.getSecretKey();
        String url = " https://api.weixin.qq.com/cgi-bin/token?" +
            "appid=" + appId + "&" +
            "secret=" + appKey + "&" +
            "grant_type=client_credential";
        String content  = HttpUtils.doGet(url, "UTF-8", 100, 1000);
        if (StringUtils.isEmpty(content)){
            throw new ServiceException("获取access_token失败");
        }
        JSONObject json = JSONUtil.parseObj(content);;
        logger.info("getAccessToken==json==="+json.toString());
        String accessToken = json.getStr("access_token");
        if (StringUtils.isEmpty(accessToken)){
            throw new ServiceException("获取access_token失败");
        }
        RedisUtils.setCacheObject(WX_MINI_ACCESS_TOKEN,accessToken, Duration.ofHours(2));
        return accessToken;
    }

    /**
     * 手机号快速验证
     * @param code
     * @return
     */
    private String getUserPhoneNumber(String code,String openid) {
        String accessToken  = RedisUtils.getCacheObject(WX_MINI_ACCESS_TOKEN);
        if (StringUtils.isEmpty(accessToken)){
            accessToken = getAccessToken();
        }
        Map<String, String> parameters = new HashMap<>();
        parameters.put("code",code);
       //parameters.put("openid",openid);
        String url = " https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" +accessToken;
       // HttpResponse httpResponse =HttpRequest.post(url).body(JSONUtil.toJsonStr(parameters)).execute();
        return HttpUtils.doPostWithJson(url,parameters);
    }

    public String getPhoneNumber(String code,String openid){
        String content = getUserPhoneNumber(code,openid);
        if (StringUtils.isEmpty(content)){
            throw new ServiceException("获取手机号失败");
        }
        JSONObject json = JSONUtil.parseObj(content);;
        logger.info("getuserphonenumber==json==="+json.toString());
        String errcode = json.getStr("errcode");
        if(!"0".equals(errcode)){
            throw new ServiceException("获取手机号失败"+json.getStr("errmsg"));
        }
        JSONObject jsonPhoneInfo = json.get("phone_info",JSONObject.class);
       String phoneNumber =  jsonPhoneInfo.getStr("phoneNumber");
        return phoneNumber;
    }

    public Map bindPhone(MemberDetailVO memberDetailVO,String mobile,String token) {
        Map res = new HashMap(16);
        WxUserVO wxUserVO = getWxUserByOpenId(memberDetailVO.getOpenId());
        if(ObjectUtil.isEmpty(wxUserVO)){
            res.put("bindPhone","fail");
            res.put("reason","未找到用户信息");
            return res;
        }
        //绑定手机号
        WxUserDO wxUserDO = new WxUserDO();
        BeanUtil.copyProperties(wxUserVO,wxUserDO);
        wxUserDO.setMobile(mobile);
        wxUserMapper.updateById(wxUserDO);

        //修改member
        QueryWrapper<MemberDO> wrapper = Wrappers.query();
        MemberDO memberDO = new MemberDO();
        wrapper.eq("wx_user_id",wxUserVO.getId());
        wrapper.eq("user_id",memberDetailVO.getUserId());
        wrapper.eq("deleted",0);
        memberDO.setMobile(mobile);
        memberMapper.update(memberDO,wrapper);
        //重新缓存member
        MemberDetailVO memberDetail =  memberMapper.selectDetailById(memberDetailVO.getId());
        RedisUtils.setCacheObject(WX_MINI_SESSION_MEMBER+token,memberDetail, Duration.ofDays(2));
        res.put("bindPhone","success");
        res.put("phone",mobile);
        wxUserVO.setMobile(mobile);
        res.put("wxUser",wxUserVO);
        return res;
    }

    @Override
    public String generateUrlLink(String path,String query) {
        String result = null;
        try {
            // 接口调用凭证：accessToken
            String accessToken = this.getAccessToken();
            System.out.println(accessToken);

            String baseUrl = "https://api.weixin.qq.com/wxa/generate_urllink?access_token="+accessToken;
            HashMap<String, Object> requestParam = new HashMap<>();
            //跳转自己小程序的链接
            requestParam.put("path",path);
            //带参 例如：id=1
            requestParam.put("query",query);
            String jsonStr = JSONUtil.toJsonStr(requestParam);
            cn.hutool.http.HttpResponse response = HttpRequest.post(baseUrl)
                .header(Header.CONTENT_ENCODING, "UTF-8")
                .header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded")
                .body(jsonStr)
                .execute();
            if (response.getStatus() == HttpStatus.HTTP_OK) {
                com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(response.body());
                String url_link = jsonObject.getString("url_link");
                result =url_link;
                System.out.println(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    @Override
    public String getMiniCodeImg(String scene,String page) {
        try {
            Map<String, Object> resultObj = new HashMap();
            PrintWriter out = null;
            InputStream in = null;
            String result = "";
            // 接口调用凭证：accessToken
            String accessToken = this.getAccessToken();
            try {
                URL realUrl = new URL("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token="+accessToken);
                // 打开和URL之间的连接
                URLConnection conn = realUrl.openConnection();
                // 设置通用的请求属性
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
                // 发送POST请求必须设置如下两行
                conn.setDoOutput(true);
                conn.setDoInput(true);
                // 获取URLConnection对象对应的输出流
                out = new PrintWriter(conn.getOutputStream());
                // 发送请求参数
                JSONObject jsonObject = new JSONObject();
                //参数自定义
                jsonObject.put("scene", scene);
                //要生成小程序码的链接
                if (page != null) {
                    jsonObject.put("page",page);
                }
                //二维码的宽度
                jsonObject.put("width", 430);
                //自动配置线条颜色，如果颜色依然是黑色，则说明不建议配置主色调
                jsonObject.put("auto_color", false);
                //是否需要透明底色， is_hyaline 为true时，生成透明底色的小程序码
                jsonObject.put("is_hyaline", false);
                //要打开的小程序版本。正式版为 "release"，体验版为 "trial"，开发版为 "develop"
                jsonObject.put("env_version", "release");
                //检查page 是否存在，为 true 时 page 必须是已经发布的小程序存在的页面（否则报错）；为 false 时允许小程序未发布或者 page 不存在， 但page 有数量上限（60000个）请勿滥用
                jsonObject.put("check_path", false);
                out.print(jsonObject);
                // flush输出流的缓冲
                out.flush();
                in = conn.getInputStream();
                byte[] data = null;
                // 读取图片字节数组
                try {
                    ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
                    byte[] buff = new byte[100];
                    int rc = 0;
                    while ((rc = in.read(buff, 0, 100)) > 0) {
                        swapStream.write(buff, 0, rc);
                    }
                    data = swapStream.toByteArray();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                return new String(org.apache.commons.net.util.Base64.encodeBase64(data));
            } catch (Exception e) {
                System.out.println("发送 POST 请求出现异常！" + e);
                e.printStackTrace();
            }
            // 使用finally块来关闭输出流、输入流
            finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
